﻿# Shared.py: Alien Xenocide shared functions and classes
# Written by Victor Luna Freire - victorclf@live.com
# Copyright (c) Victor Luna Freire. All rights reserved.
# Sunday, June 24, 2009 00:00:00 PM
# Python 2.6.2
import Maps
import Defines
import math

# Functions to convert from unit coordinates to grid coordinates
# CUBE_SIZE hardcoded for maximum speed
def togrid(pos):
    ''' Converts from unit coordinate to grid coordinate. Defines.CUBE_SIZE of 128. '''
    return (pos[0] >> 7, pos[1] >> 7)

def togridF(pos):
    ''' Converts from unit coordinate to grid coordinate. Casts to int! Defines.CUBE_SIZE of 128. '''
    return (int(pos[0]) >> 7, int(pos[1]) >> 7)
    

def tounit(pos):
    ''' Converts from grid coordinate to unit coordinate. Defines.CUBE_SIZE of 128. '''
    return (pos[0] << 7, pos[1] << 7)
    

def gridcenter(pos):
    ''' Returns the center of the grid where the point received is. '''
    return (int(pos[0] / Defines.CUBE_SIZE) * Defines.CUBE_SIZE + (Defines.CUBE_SIZE / 2),
            int(pos[1] / Defines.CUBE_SIZE) * Defines.CUBE_SIZE + (Defines.CUBE_SIZE / 2))

    
# Special horizontal and vertical ray casters for collision detection
def cast_vertical_ray(pos, ray_angle):
        '''
        Returns vertical intersection  

        pos - tuple with player's x and y position
        ray_angle - current ray angle
        '''
        
        points_hit = 1
        
        # * integer casts are for rounding down
        # There's no intersection if the ray is parallel to the vertical grid lines
        
        ray_facing_left = (90 < ray_angle < 270)
        ray_angle_rad = math.radians(ray_angle)

        # Finds the X of the first intersection point, then decides whether line belongs to left or right block, do not remove round down
        first_point_x = int(pos[0] / Defines.CUBE_SIZE) * Defines.CUBE_SIZE
        if ray_facing_left:
            first_point_x -= 1                  #belongs to left block
        else:
            first_point_x += Defines.CUBE_SIZE     #belongs to right block
        
        # Player Y plus the its distance to first intersection point, rounded down
        first_point_y_var = ( (pos[0] - first_point_x) * math.tan(ray_angle_rad) )
        first_point_y = int(pos[1] + first_point_y_var)
        
        # Is the first intersection point inside the world?
        firstpointgrid = togrid((first_point_x, first_point_y))
        
        if not Maps.Maps.is_valid_grid(firstpointgrid):
            return points_hit

        # If there's a wall on the first point hit, return the position else continue searching for walls
        if Maps.Maps.wall_onG(firstpointgrid): return points_hit
        
        # Distance between subsequent intersection points
        # A MINUS HERE SINCE THE Y is inverted from cartesian, either form breaks in certain situations
        #DEBUGNOTE the minus seems correct problem might be in the horizontal intersect

        # The y variation for the next intersections must have the same sign as the first point y variation
        y_variation = abs(int(Defines.CUBE_SIZE * math.tan(ray_angle_rad)))
        if first_point_y_var < 0:
            y_variation = -y_variation

        if ray_facing_left:
            x_variation = -Defines.CUBE_SIZE 
        else:
            x_variation = Defines.CUBE_SIZE
        
        # Search for a intersection point until grid index is out of bounds
        last_point = (first_point_x, first_point_y)
        
        while 1:
            points_hit += 1
            next_point = (last_point[0] + x_variation, last_point[1] + y_variation)
            next_point_grid = togrid(next_point)
            
            if not Maps.Maps.is_valid_grid(next_point_grid):
                return points_hit # reached end of the world without finding any walls

            if Maps.Maps.wall_onG(next_point_grid):
                return points_hit
            
            # This intersection point does not belong to a wall, proceed to next
            last_point = next_point

        
        assert 0, "Should not reach this point!"
    
    
def cast_horizontal_ray(pos, ray_angle):
        '''
        Returns horizontal intersection  

        pos - tuple with player's x and y position
        ray_angle - current ray angle
        '''
       
        points_hit = 1
        
        # * integer casts are for rounding down

        # There's no intersection if the ray is parallel to the horizontal grid lines
        if ray_angle in (0, 180):  return points_hit
        
        ray_facing_up = (0 < ray_angle < 180)
        ray_angle_rad = math.radians(ray_angle)
        
        # Finds the Y of the first intersection point, then decides whether line belongs to upper or lower block, do not remove round down
        first_point_y = int(pos[1] / Defines.CUBE_SIZE) * Defines.CUBE_SIZE

        if ray_facing_up:
            first_point_y -= 1                   #belongs to upper block
        else:
            first_point_y += Defines.CUBE_SIZE      #belongs to lower block
        
        # Player X plus its the distance to first intersection point, rounded down
        first_point_x_var =  ( (pos[1] - first_point_y) / math.tan(ray_angle_rad) )
        first_point_x = int(pos[0] + first_point_x_var)
        
        firstpointgrid = togrid((first_point_x, first_point_y))
        # Is the first intersection point inside the world?
        if not Maps.Maps.is_valid_grid(firstpointgrid):
            return points_hit
                                
        # If there's a wall on the first point hit, return the position else continue searching for walls
        #if self.wall_onG(firstpointgrid): return (first_point_x, first_point_y)
        if Maps.Maps.wall_onG(firstpointgrid): return points_hit

        # Distance between subsequent intersection points
        #DEBUG: analyzing the sign of the x_variation, is it really NEGATIVE and not positive as described in the text?
        
        x_variation = abs(int(Defines.CUBE_SIZE / math.tan(ray_angle_rad)))
        if first_point_x_var < 0: #the sign of the 2nd variation MUST be the same as the 1st!
            x_variation = -x_variation

        if ray_facing_up:
            y_variation = -Defines.CUBE_SIZE 
        else:
            y_variation = Defines.CUBE_SIZE

        # Search for a intersection point until grid index is out of bounds
        last_point = (first_point_x, first_point_y) 
        while 1:
            points_hit += 1
            next_point = (last_point[0] + x_variation, last_point[1] + y_variation)
            next_point_grid = togrid(next_point)

            if not Maps.Maps.is_valid_grid(next_point_grid):
                return points_hit # reached end of the world without finding any walls

            if Maps.Maps.wall_onG(next_point_grid):
                return points_hit
            
            # This intersection point does not belong to a wall, proceed to next
            last_point = next_point

        
        assert 0, "Should not reach this point!"




    
    